//
// Created by YueChuan on 2023/4/10.
//

#include "address.h"

namespace yuechuan {
    static Logger::ptr g_logger = YUECHUAN_LOG_NAME("root");

    /**
     * @brief 创建多少位的掩码
     * @tparam T
     * @param bits 掩码的位数
     * @return 比特位全为1的掩码
     */
    template<class T>
    static T CreateMask(uint32_t bits) {
        return (1 << (sizeof(T) * 8 - bits)) - 1;
    }

    /**
     * @brief 获取一个二进制数据中1的个数
     * @tparam T 要计数的类型
     * @param value 要计数的值
     * @return uint32_t 1的个数
     */
    template<class T>
    static uint32_t CountBytes(T value) {
        uint32_t result = 0;
        for (; value; ++result) {
            value &= value - 1;
        }
        return result;
    }

    Address::ptr Address::Create(const sockaddr *addr, socklen_t addrLen) {
        if (addr == nullptr) {
            return nullptr;
        }
        Address::ptr result;
        switch (addr->sa_family) {
            case AF_INET:
                result.reset(new IPV4Address(*(const sockaddr_in *) addr));
                break;
            case AF_INET6:
                result.reset(new IPV6Address(*(const sockaddr_in6 *) addr));
                break;

            default:
                result.reset(new UnknowAddress(*addr));
                break;
        }
        return result;
    }

    bool Address::Lookup(std::vector<Address::ptr> &result, const std::string &host, int family, int type,
                         int protocol) {
        addrinfo hints, *results, *next;
        hints.ai_flags = 0;
        hints.ai_family = family;
        hints.ai_socktype = type;
        hints.ai_protocol = protocol;
        hints.ai_addrlen = 0;
        hints.ai_canonname = nullptr;
        hints.ai_next = nullptr;

        std::string node;
        const char *service = nullptr;

        // 检查IPV6 service
        if (!host.empty() && host[0] == '[') {
            const char *endipv6 = (const char *) memchr(host.c_str() + 1, ']', host.size() - 1);
            if (endipv6) {
                if (*(endipv6 + 1) == ':') {
                    service = endipv6 + 2;
                }
                node = host.substr(1, service - host.c_str() - 1);

            }
        }
        // 检查 node service
        if (node.empty()) {
            service = (const char *) memchr(host.c_str(), ':', host.size());
            if (service) {
                if (!memchr(service + 1, ':', host.c_str() + host.size() - service - 1)) {
                    node = host.substr(0, service - host.c_str());
                    ++service;
                }
            }
        }
        if (node.empty()) {
            node = host;
        }
        int error = getaddrinfo(node.c_str(), service, &hints, &results);
        if (error) {
            YUECHUAN_LOG_ERROR(g_logger) << "Address:Lookup Error " << error << " : " << gai_strerror(error);
            return false;
        }

        next = results;
        while (next) {
            result.push_back(Create(next->ai_addr, (socklen_t) next->ai_addrlen));
            next = next->ai_next;
        }
        freeaddrinfo(results);
        freeaddrinfo(next);
        return true;
    }

    Address::ptr Address::LookupAny(const std::string &host, int family, int type,
                                    int protocol) {
        std::vector<Address::ptr> result;
        if (Lookup(result, host, family, type, protocol)) {
            return result[0];
        }
        return nullptr;
    }

    std::shared_ptr<IPAddress> Address::LookupAnyIPAddress(const std::string &host, int family, int type,
                                                           int protocol) {
        std::vector<Address::ptr> result;
        if (Lookup(result, host, family, type, protocol)) {
            for (auto &i : result) {
                IPAddress::ptr v = std::dynamic_pointer_cast<IPAddress>(i);
                if (v) {
                    return v;
                }
            }
        }
        return nullptr;
    }

    bool Address::GetInterfaceAddress(std::multimap<std::string, std::pair<Address::ptr, uint32_t> > &result,
                                      int family) {
        ifaddrs *next, *results;
        if (getifaddrs(&results) != 0) {
            YUECHUAN_LOG_ERROR(g_logger) << "Address::GetInterfaceAddress getifaddrs " << "err = " << errno << " : "
                                         << strerror(errno);
            return false;
        }
        try {
            for (next = results; next; next = next->ifa_next) {
                Address::ptr addr;
                uint32_t prefix_length = ~0u;
                if (family != AF_UNSPEC && family != next->ifa_addr->sa_family) {
                    continue;
                }
                switch (next->ifa_addr->sa_family) {
                    case AF_INET: {
                        addr = Create(next->ifa_addr, sizeof(sockaddr_in));
                        uint32_t netmask = ((sockaddr_in *) next->ifa_netmask)->sin_addr.s_addr;
                        prefix_length = CountBytes(netmask);
                    }
                        break;
                    case AF_INET6: {
                        addr = Create(next->ifa_addr, sizeof(sockaddr_in6));
                        in6_addr &netmask = ((sockaddr_in6 *) next->ifa_netmask)->sin6_addr;
                        prefix_length = 0;
                        for (int i = 0; i < 16; ++i) {
                            prefix_length += CountBytes(netmask.s6_addr[i]);
                        }

                    }
                        break;
                    default:
                        break;
                }
                if (addr) {
                    result.insert(std::make_pair(next->ifa_name, std::make_pair(addr, prefix_length)));
                }
            }
        } catch (...) {
            YUECHUAN_LOG_ERROR(g_logger) << "address::GetInterfaceAddress exception ";
            freeifaddrs(results);
            return false;
        }
        freeifaddrs(results);
        freeifaddrs(next);
        return !result.empty();
    }

    bool Address::GetInterfaceAddresses(std::vector<std::pair<Address::ptr, uint32_t>> &result, std::string &iface,
                                        int family) {
        if (iface.empty() || iface == "*") {
            if (family == AF_INET || family == AF_UNSPEC) {
                result.emplace_back(std::make_pair(Address::ptr(new IPV4Address()), 0u));
            }
            if (family == AF_INET6 || family == AF_UNSPEC) {
                result.emplace_back(std::make_pair(Address::ptr(new IPV6Address()), 0u));
            }
            return true;
        }
        std::multimap<std::string, std::pair<Address::ptr, uint32_t> > results;
        if (!GetInterfaceAddress(results, family)) {
            return false;
        }
        auto its = results.equal_range(iface);
        for (; its.first != its.second; ++its.first) {
            result.emplace_back(its.first->second);
        }
        return !result.empty();
    }

    int Address::getFamily() const {
        return getAddress()->sa_family;
    }

    std::string Address::toString() const {
        std::stringstream ss;
        insert(ss);
        return ss.str();
    }

    bool Address::operator<(const Address &rhs) const {
        socklen_t minLen = std::min(getAddrLen(), rhs.getAddrLen());
        int result = memcmp(getAddress(), rhs.getAddress(), minLen);
        if (result < 0) {
            return true;
        } else if (result > 0) {
            return false;
        } else if (getAddrLen() < rhs.getAddrLen()) {
            return true;
        }
        return false;
    }

    bool Address::operator==(const Address &rhs) const {
        bool result = getAddrLen() == rhs.getAddrLen();
        if (!result) {
            return false;
        } else {
            return memcmp(getAddress(), rhs.getAddress(), getAddrLen()) == 0;
        }

    }

    bool Address::operator!=(const Address &rhs) const {
        return !(*this == rhs);
    }

    IPAddress::ptr IPAddress::Create(const char *address, const uint16_t port) {
        addrinfo hints, *result;
        memset(&hints, 0, sizeof(hints));

        hints.ai_flags = AI_NUMERICHOST;
        hints.ai_family = AF_UNSPEC;

        int error = getaddrinfo(address, nullptr, &hints, &result);
        if (error) {
            YUECHUAN_LOG_ERROR(g_logger) << " IPAddress::Create Error "
                                         << errno << " : " << strerror(errno);
            return nullptr;
        }

        try {
            IPAddress::ptr ipAddress = std::dynamic_pointer_cast<IPAddress>(
                    Address::Create(result->ai_addr, (socklen_t) result->ai_addrlen));
            if (ipAddress) {
                ipAddress->setPort(port);
            }
            freeaddrinfo(result);
            return ipAddress;
        } catch (...) {
            freeaddrinfo(result);
            return nullptr;
        }
    }

    IPV4Address::IPV4Address(const sockaddr_in &addr) {
        m_addr = addr;
    }

    IPV4Address::IPV4Address(uint32_t address, uint16_t port) {
        memset(&m_addr, 0, sizeof(m_addr));
        m_addr.sin_family = AF_INET;
        // 端口号分为网络字节序和本地字节序，网络字节序采用大端序，本地字节序采用小端序
        m_addr.sin_port = byteswapOnLittleEndian(port);
        m_addr.sin_addr.s_addr = byteswapOnLittleEndian(address);


    }

    IPV4Address::ptr IPV4Address::Create(const char *address, uint16_t port) {
        IPV4Address::ptr rt(new IPV4Address);
        rt->m_addr.sin_port = byteswapOnLittleEndian(port);
        int result = inet_pton(AF_INET, address, &rt->m_addr.sin_addr);
        if (result == 0) {
            YUECHUAN_LOG_ERROR(g_logger) << "Create Address from char* error " << errno << " : " << strerror(errno);
            return nullptr;
        }
        return rt;
    }

    const sockaddr *IPV4Address::getAddress() const {
        return (sockaddr *) &m_addr;
    }

    sockaddr *IPV4Address::getAddress() {
        return (sockaddr *) &m_addr;
    }

    socklen_t IPV4Address::getAddrLen() const {
        return sizeof(m_addr);
    }

    std::ostream &IPV4Address::insert(std::ostream &os) const {
        uint32_t addr = byteswapOnLittleEndian(m_addr.sin_addr.s_addr);
        os << ((addr >> 24) & 0xff) << "."
           << ((addr >> 16) & 0xff) << "."
           << ((addr >> 8) & 0xff) << "."
           << (addr & 0xff);

        os << ":" << byteswapOnLittleEndian(m_addr.sin_port);
        return os;
    }


    IPAddress::ptr IPV4Address::broadcastAddress(uint32_t prefix_len) {
        if (prefix_len > 32) {
            return nullptr;
        }
        sockaddr_in broadcastAddr(m_addr);
        broadcastAddr.sin_addr.s_addr |= byteswapOnLittleEndian(CreateMask<uint32_t>(prefix_len));

        return std::make_shared<IPV4Address>(broadcastAddr);
    }

    IPAddress::ptr IPV4Address::networdAddress(uint32_t prefix_len) {

        if (prefix_len > 32) {
            return nullptr;
        }
        sockaddr_in broadcastAddr(m_addr);
        broadcastAddr.sin_addr.s_addr &= byteswapOnLittleEndian(CreateMask<uint32_t>(prefix_len));

        return std::make_shared<IPV4Address>(broadcastAddr);

    }

    IPAddress::ptr IPV4Address::subnetMask(uint32_t prefix_len) {
        sockaddr_in subnet;
        memset(&subnet, 0, sizeof(subnet));
        subnet.sin_family = AF_INET;
        subnet.sin_addr.s_addr = ~byteswapOnLittleEndian(CreateMask<uint32_t>(prefix_len));
        return std::make_shared<IPV4Address>(subnet);
    }

    uint16_t IPV4Address::getPort() const {
        return byteswapOnLittleEndian(m_addr.sin_port);
    }

    void IPV4Address::setPort(uint16_t p) {
        m_addr.sin_port = byteswapOnLittleEndian(p);
    }

    IPV6Address::IPV6Address() {
        memset(&m_addr, 0, sizeof(m_addr));
        m_addr.sin6_family = AF_INET6;
    }

    IPV6Address::IPV6Address(const sockaddr_in6 &addr) {
        m_addr = addr;
    }

    IPV6Address::IPV6Address(const uint8_t *address, uint16_t port) {
        memset(&m_addr, 0, sizeof(m_addr));
        m_addr.sin6_family = AF_INET6;
        m_addr.sin6_port = byteswapOnLittleEndian(port);
        memcpy(&m_addr.sin6_addr.s6_addr, address, 16);
    }

    IPV6Address::ptr IPV6Address::Create(const char *address, uint16_t port) {
        IPV6Address::ptr rt(new IPV6Address);
        rt->m_addr.sin6_port = byteswapOnLittleEndian(port);
        int result = inet_pton(AF_INET6, address, &rt->m_addr.sin6_addr);
        if (result <= 0) {
            YUECHUAN_LOG_ERROR(g_logger) << "create IPV6Address error from char* to object"
                                         << errno << " : " << strerror(errno);
            return nullptr;
        }
        return rt;
    }

    const sockaddr *IPV6Address::getAddress() const {
        return (sockaddr *) &m_addr;
    }

    sockaddr *IPV6Address::getAddress() {
        return (sockaddr *) &m_addr;
    }

    socklen_t IPV6Address::getAddrLen() const {
        return sizeof(m_addr);
    }

    std::ostream &IPV6Address::insert(std::ostream &os) const {
        os << "[";
        uint16_t *addr = (uint16_t *) m_addr.sin6_addr.s6_addr;
        bool used_zeros = false;
        for (size_t i = 0; i < 8; ++i) {
            if (addr[i] == 0 && !used_zeros) {
                continue;
            }
            if (i && addr[i - 1] == 0 && !used_zeros) {
                os << ":";
                used_zeros = true;
            }
            if (i) {
                os << ":";
            }
            os << std::hex << (int) byteswapOnLittleEndian(addr[i]) << std::dec;

        }
        if (!used_zeros && addr[7] == 0) {
            os << "::";
        }
        os << "]:" << byteswapOnLittleEndian(m_addr.sin6_port);
        return os;
    }


    IPAddress::ptr IPV6Address::broadcastAddress(uint32_t prefix_len) {
        sockaddr_in6 broadcastAddr(m_addr);
        broadcastAddr.sin6_addr.s6_addr[prefix_len / 8] |= CreateMask<uint8_t>(prefix_len % 8);
        for (int i = prefix_len / 8 + 1; i < 16; ++i) {
            broadcastAddr.sin6_addr.s6_addr[i] = 0xff;
        }
        return std::make_shared<IPV6Address>(broadcastAddr);
    }

    IPAddress::ptr IPV6Address::networdAddress(uint32_t prefix_len) {
        sockaddr_in6 broadcastAddr(m_addr);
        broadcastAddr.sin6_addr.s6_addr[prefix_len / 8] &= CreateMask<uint8_t>(prefix_len % 8);
        for (int i = prefix_len / 8 + 1; i < 16; ++i) {
            broadcastAddr.sin6_addr.s6_addr[i] = 0x00;
        }
        return std::make_shared<IPV6Address>(broadcastAddr);
    }

    IPAddress::ptr IPV6Address::subnetMask(uint32_t prefix_len) {
        sockaddr_in6 broadcastAddr(m_addr);
        broadcastAddr.sin6_addr.s6_addr[prefix_len / 8] = ~CreateMask<uint8_t>(prefix_len % 8);
        for (int i = prefix_len / 8 + 1; i < 16; ++i) {
            broadcastAddr.sin6_addr.s6_addr[i] = 0xff;
        }
        return std::make_shared<IPV6Address>(broadcastAddr);
    }

    uint16_t IPV6Address::getPort() const {
        return byteswapOnLittleEndian(m_addr.sin6_port);
    }

    void IPV6Address::setPort(uint16_t p) {
        m_addr.sin6_port = byteswapOnLittleEndian(p);
    }

    static const size_t MAX_PATH_LEN = sizeof(((sockaddr_un *) nullptr)->sun_path) - 1;

    UnixAddress::UnixAddress() {
        memset(&m_addr, 0, sizeof(m_addr));
        m_addr.sun_family = AF_UNIX;
        m_length = offsetof(sockaddr_un, sun_path) + MAX_PATH_LEN;

    }

    UnixAddress::UnixAddress(const std::string &path) {
        memset(&m_addr, 0, sizeof(m_addr));
        m_addr.sun_family = AF_UNIX;
        m_length = path.size() + 1;
        if (!path.empty() && path[0] == '\0') {
            --m_length;
        }
        if (m_length > sizeof(m_addr.sun_path)) {
            throw std::logic_error("path too long");
        }
        memcpy(m_addr.sun_path, path.c_str(), m_length);
        m_length += offsetof(sockaddr_un, sun_path);
    }

    const sockaddr *UnixAddress::getAddress() const {
        return (sockaddr *) &m_addr;
    }

    sockaddr *UnixAddress::getAddress() {
        return (sockaddr *) &m_addr;
    }

    socklen_t UnixAddress::getAddrLen() const {
        return m_length;
    }

    void UnixAddress::setAddrLen(uint32_t length) {
        m_length = length;
    }

    std::ostream &UnixAddress::insert(std::ostream &os) const {
        if (m_length > offsetof(sockaddr_un, sun_path) && m_addr.sun_path[0] == '\0') {
            return os << "\\0" << std::string(m_addr.sun_path + 1, m_length - offsetof(sockaddr_un, sun_path) - 1);
        }
        return os << m_addr.sun_path;
    }

    UnknowAddress::UnknowAddress(const sockaddr &addr) {
        m_addr = addr;
    }

    UnknowAddress::UnknowAddress(int family) {
        memset(&m_addr, 0, sizeof(m_addr));
        m_addr.sa_family = family;
    }

    const sockaddr *UnknowAddress::getAddress() const {
        return &m_addr;
    }

    sockaddr *UnknowAddress::getAddress() {
        return &m_addr;
    }

    socklen_t UnknowAddress::getAddrLen() const {
        return sizeof(m_addr);
    }


    std::ostream &UnknowAddress::insert(std::ostream &os) const {
        os << "[ UnknowAddress family = " << m_addr.sa_family << " ]";
        return os;
    }

    std::ostream &operator<<(std::ostream &os, const Address &addr) {
        return addr.insert(os);
    }
}// namespace yuechuan